package org.foreveross.jbpm.applicationImpl.jbpm;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;

import org.drools.definition.process.Node;
import org.drools.impl.KnowledgeBaseImpl;
import org.drools.persistence.info.WorkItemInfo;
import org.drools.process.instance.WorkItem;
import org.drools.process.instance.WorkItemManager;
import org.drools.process.instance.impl.WorkItemImpl;
import org.drools.runtime.Environment;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.process.NodeInstance;
import org.drools.runtime.process.ProcessInstance;
import org.foreveross.jbpm.application.JBPMApplication;
import org.foreveross.jbpm.application.vo.HistoryLogVo;
import org.foreveross.jbpm.application.vo.JBPMNode;
import org.foreveross.jbpm.application.vo.MessageLogVO;
import org.foreveross.jbpm.application.vo.ProcessInstanceVO;
import org.foreveross.jbpm.application.vo.ProcessVO;
import org.foreveross.jbpm.application.vo.TaskVO;
import org.foreveross.jbpm.applicationImpl.util.JbpmSupport;
import org.foreveross.jbpm.core.HistoryLog;
import org.foreveross.jbpm.core.JoinAssign;
import org.foreveross.jbpm.core.KoalaProcessInfo;
import org.foreveross.jbpm.core.MessageLog;
import org.foreveross.jbpm.core.service.JBPMTaskService;
import org.foreveross.jbpm.infra.ImageUtil;
import org.foreveross.jbpm.infra.XmlParseUtil;
import org.jbpm.process.audit.JPAProcessInstanceDbLog;
import org.jbpm.process.audit.ProcessInstanceLog;
import org.jbpm.process.instance.InternalProcessRuntime;
import org.jbpm.ruleflow.core.RuleFlowProcess;
import org.jbpm.ruleflow.instance.RuleFlowProcessInstance;
import org.jbpm.task.AccessType;
import org.jbpm.task.Content;
import org.jbpm.task.OrganizationalEntity;
import org.jbpm.task.PeopleAssignments;
import org.jbpm.task.Status;
import org.jbpm.task.Task;
import org.jbpm.task.TaskService;
import org.jbpm.task.User;
import org.jbpm.task.query.TaskSummary;
import org.jbpm.task.service.ContentData;
import org.jbpm.workflow.core.node.HumanTaskNode;
import org.jbpm.workflow.core.node.WorkItemNode;
import org.jbpm.workflow.instance.node.HumanTaskNodeInstance;
import org.mvel2.MVEL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.dayatang.domain.InstanceFactory;
import com.dayatang.querychannel.service.QueryChannelService;
import javax.jws.WebService;
/**
 * 流程的实现
 * 
 * @author lingen
 * 
 */
@Named("jbpmApplication")
@Transactional(propagation=Propagation.NESTED)
public class JBPMApplicationImpl implements JBPMApplication {

	private DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
	private static QueryChannelService queryChannel;

	public static QueryChannelService getQueryChannel() {
		if (queryChannel == null) {
			queryChannel = InstanceFactory
					.getInstance(QueryChannelService.class);
		}
		return queryChannel;
	}
	
	private static final Logger logger = LoggerFactory
			.getLogger(JBPMApplicationImpl.class);

	@Inject
	private JbpmSupport jbpmSupport;

	@Inject
	private JBPMTaskService jbpmTaskService;

	
	public byte[] getPorcessImageStream(long instanceId){
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		RuleFlowProcessInstance in = (RuleFlowProcessInstance)ksession.getProcessInstance(instanceId);
		List<Integer> nodes = new ArrayList<Integer>();
		String processName = null;
		if(in!=null){
		Collection<org.drools.runtime.process.NodeInstance> actives = in.getNodeInstances();
		processName = in.getProcessId();
		for(NodeInstance active:actives){
				nodes.add((Integer)active.getNode().getMetaData().get("x"));
				nodes.add((Integer)active.getNode().getMetaData().get("y"));
		}}else{
			ProcessInstanceLog log = JPAProcessInstanceDbLog.findProcessInstance(instanceId);
			processName = log.getProcessId();
		}
		
		KoalaProcessInfo processInfo = getQueryChannel().querySingleResult("select k from KoalaProcessInfo k where k.processId = ? ", new Object[]{processName});
		
		return ImageUtil.getProcessPictureByte(processName,processInfo.getPng(),nodes);
	}
	

	public List<TaskVO> queryTodoList(String user) {
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		TaskService taskService = jbpmSupport.getTaskService();
		List<TaskSummary> tasks = taskService.getTasksAssignedAsPotentialOwner(
				user, "en-UK");
		
		List<TaskVO> todos = new ArrayList<TaskVO>();

		Collection<org.drools.definition.process.Process> processes = ksession.getKnowledgeBase().getProcesses();
		List<String> processList = new ArrayList<String>();
		for(org.drools.definition.process.Process process:processes){
			processList.add(process.getId());
		}
		for (TaskSummary task : tasks) {
			
	        
			long processId = task.getProcessInstanceId();
			RuleFlowProcessInstance in = null;
			try {
				if(!processList.contains(task.getProcessId()))continue;
				ProcessInstance instance = ksession
						.getProcessInstance(processId);
				if (instance == null)
					continue;
				in = (RuleFlowProcessInstance) instance;
//				WorkItem workItem = ((WorkItemManager) in
//		                .getKnowledgeRuntime().getWorkItemManager()).getWorkItem(contentId);
//				Object obj = in.getNodeInstances(workItem.getId());
//				System.out.println(workItem.getParameters());
				
			} catch (Exception e) {
				continue;
			}
			ProcessInstanceLog log = JPAProcessInstanceDbLog.findProcessInstance(processId);
			TaskVO todo = new TaskVO();
			Map<String,Object> processParams = in.getVariables();
			String processData = XmlParseUtil.paramsToXml(processParams);
			todo.setActualOwner(task.getActualOwner().getId());
			todo.setActualName(task.getName());
			todo.setProcessInstanceId(processId);
			todo.setProcessId(in.getProcessId());
			try{todo.setProcessName(in.getProcessName());}catch(Exception e){todo.setProcessName(in.getProcessId());}
			todo.setTaskId(task.getId());
			todo.setProcessData(processData);
			todo.setCreateDate(df.format(log.getStart()));
			//todo.setCreater((String)in.getVariable("_process_creater"));
			todos.add(todo);
		}
		return todos;
	}
	
	/**
	 * 查询已办任务
	 */
	public List<TaskVO> queryDoenTask(String user) {
		List<TaskVO> dones = new ArrayList<TaskVO>();
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		List<HistoryLog> logs = getQueryChannel().queryResult("select log from HistoryLog log where log.user = ?", new Object[]{user});
		List<Long> ids = new ArrayList<Long>();
		for(HistoryLog log:logs){
			if(ids.contains(log.getProcessInstanceId()))continue;
			else{
				ids.add(log.getProcessInstanceId());
			}
			TaskVO todo = new TaskVO();
			long processId = log.getProcessInstanceId();
			ProcessInstanceLog processLog = null;
			try{processLog = JPAProcessInstanceDbLog.findProcessInstance(processId);}
			catch(Exception e){
				continue;
			}
			todo.setProcessData(log.getProcessData());
			todo.setProcessInstanceId(processId);
			todo.setProcessId(processLog.getProcessId());
			//TODO 存进去，关于流程名称
			todo.setProcessName(processLog.getProcessId());
			todo.setCreateDate(df.format(processLog.getStart()));
			dones.add(todo);
		}
		return dones;
	}
	
	public long startProcess(String processName, String paramsString) {
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		Map<String, Object> params = XmlParseUtil.xmlToPrams(paramsString);
		String activeProcessName = jbpmSupport.getActiveProcess(processName);
		ProcessInstance instance = ksession.startProcess(activeProcessName, params);
		ksession.fireAllRules();
		return instance.getId();
	}
	
	public void delegate(long taskId,String userId,String targetUserId){
		TaskService taskService = jbpmSupport.getTaskService();
		
		taskService.delegate(taskId, userId, targetUserId);
		
		Task task  = taskService.getTask(taskId);
		
		HistoryLog log = new HistoryLog();
		log.setComment("当初任务由"+userId+"委托给"+targetUserId);
		log.setCreateDate(new Date());
		log.setNodeName("委托");
		log.setResult("当前任务被委托");
		log.setUser(userId);
		log.setProcessInstanceId(task.getTaskData().getProcessInstanceId());
		log.save();
		
	}
	
	public boolean completeTask(long processInstanceId,long taskId,String user,String params, String data) {
	
		//更新流程级的参数
		Map<String,Object> proceeParams = XmlParseUtil.xmlToPrams(params);
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		RuleFlowProcessInstance in = (RuleFlowProcessInstance)ksession.getProcessInstance(processInstanceId);
		Set<String> keys = proceeParams.keySet();
		for(String key:keys){
			in.setVariable(key, proceeParams.get(key));
		}
		
		TaskService taskService = jbpmSupport.getTaskService();
		Task task = taskService.getTask(taskId);
		
		//更新TASK参数
		Map<String,Object> dataParams = XmlParseUtil.xmlToPrams(data);
		ContentData contentData = null;
		if (data != null) {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream outs = null;
			try {
				outs = new ObjectOutputStream(bos);
				outs.writeObject(dataParams);
				outs.close();
				contentData = new ContentData();
				contentData.setContent(bos.toByteArray());
				contentData.setAccessType(AccessType.Inline);
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				if (outs != null)
					try {
						outs.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
			}
		}
		
	
		JoinAssign joginAssign = null;
		//判断是否是会签流程
		long contentId = taskService.getTask(task.getId()).getTaskData().getDocumentContentId();
		Content content = jbpmTaskService.getContent(contentId);
		byte[] conByte = content.getContent();
		ByteArrayInputStream bis = new ByteArrayInputStream (conByte);       
        try {
			ObjectInputStream ois = new ObjectInputStream (bis);
			Object obj = ois.readObject();
			Map<String,Object> map = (Map<String,Object>)obj;
			if(map.containsKey("ASSIGN")){
				
				String value = (String)map.get("ASSIGN");
				System.out.println(in.getVariables());
				joginAssign = (JoinAssign)in.getVariable(value);
				if(joginAssign.getAllCount()==0){
					joginAssign.setAllCount(task.getPeopleAssignments().getPotentialOwners().size());
				}
				
				
				//获取当前用户的决择
				String choice = (String) in.getVariable(joginAssign.getKeyChoice());
				joginAssign.addChoice(user, choice);

				HistoryLog log = new HistoryLog();
				log.setComment(user+":的选择的是:"+choice);
				log.setCreateDate(new Date());
				log.setNodeName("会签:");
				log.setResult("节点会签");
				log.setUser(user);
				log.setProcessInstanceId(task.getTaskData().getProcessInstanceId());
				log.save();
				
				//如果会签成功，流转会签
				if(joginAssign.isSuccess()){
				
					in.setVariable(joginAssign.getKeyChoice(), "1");
	            	task.getTaskData().setStatus(Status.Reserved);
					taskService.start(taskId, task.getTaskData().getActualOwner().getId());
					taskService.complete(taskId,task.getTaskData().getActualOwner().getId(), null);
				}
				
				//如果任务仍然存在，但所有人已经完成投票了，则此次会签失败
				if(joginAssign.isFinished()){
					//会签失败，将关键值置为0，完成此任务
					in.setVariable(joginAssign.getKeyChoice(), "0");
	            	task.getTaskData().setStatus(Status.Reserved);
					taskService.start(taskId,task.getTaskData().getActualOwner().getId());
					taskService.complete(taskId,task.getTaskData().getActualOwner().getId(), null);
				}
				
				this.jbpmTaskService.removeTaskUser(taskId, user);
				//.remove(new User(user));
			}
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}  
        
        if(joginAssign==null){
		try{
			taskService.start(taskId, user);
		  taskService.complete(taskId, user, contentData);
		}catch(RuntimeException e){
			e.printStackTrace();
			logger.error(taskId+":任务执行出错");
			jbpmTaskService.failedTask(task);
			return false;
		}
        }
		return true;
	}
	
	public List<HistoryLogVo> queryHistoryLog(long processId){
		List<HistoryLog> lists = getQueryChannel().queryResult("select log from HistoryLog log where log.processInstanceId = ? order by log.createDate", new Object[]{processId});
		List<HistoryLogVo> logs = new ArrayList<HistoryLogVo>();
		
		for(HistoryLog vo:lists){
			HistoryLogVo log = new HistoryLogVo();
			BeanUtils.copyProperties(vo, log);
			logs.add(log);
		}
		
		return logs;
	}

	public List<MessageLogVO> getMessages(String user) {
		List<MessageLog> lists = getQueryChannel().queryResult("select log from MessageLog log where log.user = ?", new Object[]{user});
		List<MessageLogVO> messages = new ArrayList<MessageLogVO>();
		for(MessageLog log:lists){
			MessageLogVO vo = new MessageLogVO();
			BeanUtils.copyProperties(log, vo);
			messages.add(vo);
		}
		return messages;
	}
	
	/**
	 * 将指定的流程转到指定的节点上去
	 * @param processInstanceId
	 * @param nodeId
	 */
	public void assignToNode(long processInstanceId,long nodeId){
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		RuleFlowProcessInstance in = (RuleFlowProcessInstance)ksession.getProcessInstance(processInstanceId);
		HumanTaskNode node = (HumanTaskNode)in.getNodeContainer().getNode(nodeId);
		List<WorkItemInfo> workItemInfos = jbpmTaskService.getWorkItemInfo(processInstanceId);
        HumanTaskNodeInstance human = (HumanTaskNodeInstance)in.getNodeInstance(node);
        
        //如果流程当前节点与要激活的节点一样，则不转移
        boolean isSame = true;

        
        Collection<org.drools.runtime.process.NodeInstance> instances = in.getNodeInstances();
        for(org.drools.runtime.process.NodeInstance nodeInstance:instances){
        	if(nodeInstance.getNodeId()!=human.getNodeId()){
        		isSame=false;
        	}
        }
        
        in.setVariable("REMOVE", true);
        
        
        for(org.drools.runtime.process.NodeInstance nodeInstance:instances){
        	org.jbpm.workflow.instance.NodeInstance removeNode = in.getNodeInstance(nodeInstance.getId());
        	if(removeNode!=null){
        		in.removeNodeInstance(removeNode);
        		in.removeEventListener("workItemCompleted", (HumanTaskNodeInstance)removeNode, false);
        	}
        }

        
		jbpmTaskService.exitedTask(processInstanceId);
		
		human.internalTrigger(null, "");
	 	   
		
		for(WorkItemInfo workItemInfo:workItemInfos){
		  jbpmTaskService.removeWorkItemInfo(workItemInfo);
		}
		
        if(isSame)return;
		HistoryLog log = new HistoryLog();
		log.setComment("转移到节点:"+human.getNodeName());
		log.setCreateDate(new Date());
		log.setNodeName("人工转移");
		log.setResult("当前流程任务被管理员变更");
		log.setUser("Admin");
		log.setProcessInstanceId(processInstanceId);
		log.save();
       
	}
	
	private Map<String,Object> parseVar( Map<String, Object> params, Map<String, Object> values){
		Set<String> keys = params.keySet();
		Map<String,Object> returnVal = new HashMap<String,Object>();
		for(String key:keys){
			String express = (String)params.get(key);
			if(express.contains("#{") && express.contains("}")){
				express = express.substring(express.indexOf("#{")+2,express.lastIndexOf("}"));
				Object value = MVEL.eval(express,values);
				returnVal.put(key, value);
			}
			else{
				returnVal.put(key, params.get(key));
			}
			
		}
		return returnVal;
	}
	
	public List<JBPMNode> getProcessNodes(String processId){
		List<JBPMNode> jbpmNodes = new ArrayList<JBPMNode>();
		org.drools.definition.process.Process process = jbpmSupport.getKsession().getKnowledgeBase().getProcess(processId);
		RuleFlowProcess rule = (RuleFlowProcess)process;
		Node[] nodes = rule.getNodes();
		for(Node node:nodes){
			if(node instanceof HumanTaskNode){
				JBPMNode jbpmNode = new JBPMNode(node.getId(),node.getName());
				jbpmNodes.add(jbpmNode);
			}
		}
		return jbpmNodes;
	}
	
	public List<JBPMNode> getProcessNodes(long processInstanceId){
		List<JBPMNode> jbpmNodes = new ArrayList<JBPMNode>();
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		RuleFlowProcessInstance in = (RuleFlowProcessInstance)ksession.getProcessInstance(processInstanceId);
		Node[] nodes = in.getNodeContainer().getNodes();
		for(Node node:nodes){
			if(node instanceof HumanTaskNode){
				JBPMNode jbpmNode = new JBPMNode(node.getId(),node.getName());
				HumanTaskNode human = (HumanTaskNode)node;
				jbpmNodes.add(jbpmNode);
			}
		}
		return jbpmNodes;
	}
	
	public void repairTask(long taskId){
		TaskService taskService = jbpmSupport.getTaskService();
		Task task = taskService.getTask(taskId);
		jbpmTaskService.repairTask(task);
	}
	
	
	public List<TaskVO> queryErrorList() {
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		List<TaskSummary> tasks = jbpmTaskService.getErrorTasks();
		List<TaskVO> todos = new ArrayList<TaskVO>();

		Collection<org.drools.definition.process.Process> processes = ksession.getKnowledgeBase().getProcesses();
		List<String> processList = new ArrayList<String>();
		for(org.drools.definition.process.Process process:processes){
			processList.add(process.getId());
		}
		for (TaskSummary task : tasks) {
			long processId = task.getProcessInstanceId();
			RuleFlowProcessInstance in = null;
			try {
				if(!processList.contains(task.getProcessId()))continue;
				ProcessInstance instance = ksession
						.getProcessInstance(processId);
				if (instance == null)
					continue;
				in = (RuleFlowProcessInstance) instance;
			} catch (Exception e) {
				continue;
			}
			ProcessInstanceLog log = JPAProcessInstanceDbLog.findProcessInstance(processId);
			TaskVO todo = new TaskVO();
			Map<String,Object> processParams = in.getVariables();
			String processData = XmlParseUtil.paramsToXml(processParams);
			todo.setActualOwner(task.getActualOwner().getId());
			todo.setActualName(task.getName());
			todo.setProcessInstanceId(processId);
			todo.setProcessId(in.getProcessId());
			try{todo.setProcessName(in.getProcessName());}catch(Exception e){todo.setProcessName(in.getProcessId());}
			todo.setTaskId(task.getId());
			todo.setProcessData(processData);
			todo.setCreateDate(df.format(log.getStart()));
			//todo.setCreater((String)in.getVariable("_process_creater"));
			todos.add(todo);
		}
		return todos;
	}
	
	public List<ProcessInstanceVO> queryAllActiveProcess(String processId){
		String processIdActual = jbpmSupport.getActiveProcess(processId);
		List<ProcessInstanceVO> instances = new ArrayList<ProcessInstanceVO>();
		List<ProcessInstanceLog> logs = JPAProcessInstanceDbLog.findActiveProcessInstances(processIdActual);
		for(ProcessInstanceLog log:logs){
			StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
			RuleFlowProcessInstance in = (RuleFlowProcessInstance)ksession.getProcessInstance(log.getId());
			instances.add(getProcessInstanceVO(in,log));
		}
		return instances;
	}
	
	public ProcessInstanceVO getProcessInstance(long processId){
		StatefulKnowledgeSession ksession = jbpmSupport.getKsession();
		RuleFlowProcessInstance in = (RuleFlowProcessInstance)ksession.getProcessInstance(processId);
		ProcessInstanceLog log = JPAProcessInstanceDbLog.findProcessInstance(processId);
		return getProcessInstanceVO(in,log);
	}
	
	private ProcessInstanceVO getProcessInstanceVO(RuleFlowProcessInstance in,ProcessInstanceLog log){
        ProcessInstanceVO vo = new ProcessInstanceVO();
		vo.setCreateDate(df.format(log.getStart()));
		//vo.setCreater((String)in.getVariable("_process_creater"));
		vo.setParentProcessInstanceId(log.getParentProcessInstanceId());
		vo.setProcessId(log.getProcessId());
		vo.setProcessInstanceId(log.getProcessInstanceId());
		try{vo.setProcessName(in.getProcessName());}catch(Exception e){vo.setProcessName(in.getProcessId());}
		vo.setStatus(in.getState());
		vo.setData(XmlParseUtil.paramsToXml(in.getVariables()));
		return vo;
	}
	
	public void addProcess(String processId,int version,String data,byte[] png){
		KoalaProcessInfo processInfo = getQueryChannel().querySingleResult("select k from KoalaProcessInfo k where k.versionNum = ? and k.processName=?", new Object[]{version,processId});
		if(processInfo!=null){
			processInfo.setData(data.getBytes());
			processInfo.setPng(png);
		}else{
			processInfo = new KoalaProcessInfo(processId,version,data,png);
		}
		try{
			this.jbpmSupport.addProcessToCenter(processInfo);
			processInfo.save();
		}catch(Exception e){
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}
	}
	
	public List<ProcessVO> getProcesses(){
		List<ProcessVO> processStrings = new ArrayList<ProcessVO>();
		Collection<org.drools.definition.process.Process> processes = this.jbpmSupport.getKsession().getKnowledgeBase().getProcesses();
	    for(org.drools.definition.process.Process process:processes){
	    	ProcessVO vo = new ProcessVO();
	    	vo.setId(process.getId());
	    	vo.setName(process.getName());
	    	processStrings.add(vo);
	    }
	    return processStrings;
	}
	
	/**
	 * 删除一个流程实例
	 * @param processInstanceId
	 */
	public void removeProcessInstance(long processInstanceId){
		this.jbpmSupport.getKsession().abortProcessInstance(processInstanceId);
	}
	
	
	public static void main(String args[]){
//		JBPMApplicationImpl impl=new JBPMApplicationImpl();
//	    String address="http://localhost/jbpm";
//	    javax.xml.ws.Endpoint.publish(address, impl);
	}
	
	
}